package edu.ncsu.csc.ip.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import edu.ncsu.csc.ip.client.protocol.ProtocolClientImpl;
import edu.ncsu.csc.ip.common.Log;

/**
 * This is the main class that starts the peer interactions. It registers with
 * the RS server, starts the RFC Server and also allows for a user to provide
 * input on what RFC to download. Though that process is also be automated.
 * 
 * 
 * 
 * @author Moin Ayazifar <mayazif@ncsu.edu>
 * @author Steven Elliott <sdelliot@ncsu.edu>
 * 
 */

public class PeerClient {

	private final int HEADER_SIZE = 8;
	private int MSS = 0;
	private ProtocolClientImpl protocol = new ProtocolClientImpl();
	private int port = 65423;
	private int timeout = 3000;
	private InetAddress[] addressList = null;
	private String filename = "";
	

	public PeerClient(InetAddress[] addressList, int port, String filename, int MSS) {
		this.addressList = addressList;
		this.port = port;
		this.filename = filename;
		this.MSS = MSS;
	}
	
	public PeerClient(String[] args){		

		if(args.length < 4){
			Log.println("Proper Usage includes arguments: <IP Address> <Port#> <filename> <Max Segment Size>");
	        System.exit(0);
	    }
		
		this.addressList = new InetAddress[args.length-3];
		for(int i = 0; i < (args.length-3); i++){
			try {
				addressList[i] = InetAddress.getByName(args[i]);
			} catch (UnknownHostException e) {
				Log.println("IP Address: " + args[i] + " is malformed.");
				System.exit(0);
			}
		}
		
		try {
			port = Integer.parseInt(args[args.length-3]);
		} catch (NumberFormatException nfe) {
			Log.println("Port is not a number: " + args[args.length-3]);
			Log.println("Proper Usage includes arguments: <IP Address> <Port#> <filename> <Max Segment Size>");
			System.exit(0);
		}
		
		filename = args[args.length-2];
		
		try {
			MSS = Integer.parseInt(args[args.length-1]);
		} catch (NumberFormatException nfe) {
			Log.println("MSS is not a number: " + args[args.length-1]);
			Log.println("Proper Usage includes arguments: <IP Address> <Port#> <filename> <Max Segment Size>");
			System.exit(0);
		}
		
	}

	public void runClient() {
		long sequenceNumber = 0;
		File file = new File(filename);
		FileInputStream fileInputStream = null;
		try {
			fileInputStream = new FileInputStream(file);
		} catch (FileNotFoundException e1) {
			Log.println("File does not exist: " + filename);
			Log.println("Proper Usage includes arguments: <IP Address> <Port#> <filename> <Max Segment Size>");
			System.exit(0);
		}

		long bytesLeft = file.length();

		long numLoops = file.length() / MSS;
		if (file.length() % MSS != 0) {
			numLoops += 1;
		}

		while (sequenceNumber <= numLoops) {
			byte[] buf = new byte[MSS + HEADER_SIZE];
			byte[] header = protocol.makeDataHeader(sequenceNumber);
			
			try {
				for (int i = 0; i < header.length; i++) {
					buf[i] = header[i];
				}
				if (bytesLeft > MSS) {
					// how many bytes to write.
					fileInputStream.read(buf, HEADER_SIZE, MSS);
				} else {
					fileInputStream.read(buf, HEADER_SIZE, (int) bytesLeft);
				}

				buf = protocol.putChecksum(buf);

				List<Thread> threads = new ArrayList<Thread>();
				
				// Start timer
				long startTime = System.nanoTime();

				for (int i = 0; i < addressList.length; i++) {
					Thread t = new ClientThread(addressList[i], port, buf,
							sequenceNumber, timeout);
					t.start();
					threads.add(t);
				}
				try {
					// Waiting until all the threads complete
					for (int i = 0; i < threads.size(); i++) {
						((Thread) threads.get(i)).join();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				// End timer
				long endTime = System.nanoTime();
				long duration = (endTime - startTime)/1000000;
				if(30*duration > 1000){
					timeout = 1000;
				} else{
					timeout = (int) (30*duration);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			sequenceNumber++;
		}
		try {
			fileInputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		sendFin(sequenceNumber);
		Log.println("Finished sending packets");
	}
	
	public void sendFin(long sequenceNumber){
		byte[] fin = protocol.makeFin(sequenceNumber);
		List<Thread> threads = new ArrayList<Thread>();
		for (int i = 0; i < addressList.length; i++) {
			Thread t = new ClientThread(addressList[i], port, fin,
					sequenceNumber, timeout);
			t.start();
			threads.add(t);
		}
		try {
			// Waiting until all the threads complete
			for (int i = 0; i < threads.size(); i++) {
				((Thread) threads.get(i)).join();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
